﻿using System;
using System.Text;
using COC.Character;
using COC.Exception;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Object = UnityEngine.Object;

namespace COC.Algorithm
{
    //simply maintaining object by object pool,apply to game object,component etc,but except custom's object such Path,GridNode,Stack 
    public class PoolManager<T, E> where E : Component, new()
    {
        //record all specific type E by indicating of T
        private static Dictionary<T, List<E>> poolEntries = new Dictionary<T, List<E>>();
        Dictionary<T, Component> prefabLookup = new Dictionary<T, Component>();


        private static PoolManager<T, E> instance;

        private GameObject DEFAULT_GM = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
        private GameObject poolGameObject;
        private GameObject go;

        private static string POOL_MANAGER_TAG = "Pool_Manager";

        public static PoolManager<T, E> getInstance()
        {
            if (instance == null)
            {

                instance = new PoolManager<T, E>(GameObject.FindGameObjectWithTag(POOL_MANAGER_TAG));
            }
            return instance;
        }

        public PoolManager(GameObject target)
        {
            poolGameObject = target;
            DEFAULT_GM.SetActive(false);
            DEFAULT_GM.transform.parent = target.transform;
        }

        public event OtherParameters SetDefaultGameobject;//list of wizarded game object which according as by reference[key]=prefab id value=game object  
        public delegate GameObject OtherParameters(T prefab);
        public void addPrefab(T prefab, E obj)
        {
            obj.gameObject.transform.parent = poolGameObject.transform;
            obj.gameObject.SetActive(false);
            List<E> _components = null;
            Component c=null;
            if (!prefabLookup.ContainsKey(prefab))
            {
                prefabLookup[prefab] = obj;
               
            }
            if (!poolEntries.TryGetValue(prefab, out _components))
            {
                List<E> objsStack = new List<E>();
                objsStack.Add(obj);
                poolEntries.Add(prefab, objsStack);
            }
            else
            {
                _components.Add(obj);

            }


        }
        public E Spawn(T prefab)
        {
            return Spawn(prefab, Vector3.zero, Quaternion.identity);
        }
        public E Spawn(T prefab, Vector3 position)
        {
            return Spawn(prefab, position, Quaternion.identity);
        }
        public E Spawn(T prefab, Vector3 position, Quaternion rotation)
        {
            E obj = null;
            if (prefabLookup.ContainsKey(prefab))
            {
                if (poolEntries.Count > 0)
                {
                    List<E> objs = null;
                    if (poolEntries.TryGetValue(prefab, out objs))
                    {
                        if (objs.Count > 0)
                        {
                            while (obj == null && objs.Count > 0)
                            {
                                obj = objs[0];
                                objs.RemoveAt(0);
                                if (obj != null)
                                {
                                    UnityEngine.Debug.Log("Pool have one,get it");
                                    obj.transform.parent = null;
                                    obj.gameObject.transform.localPosition = position;
                                    obj.gameObject.transform.rotation = rotation;
                                    obj.gameObject.SetActive(true);

                                    break;
                                }
                            }

                            obj = (E)(Component)obj.transform;
                            return obj;
                        }
                        //if couldn't find it,just make a new one.

                        UnityEngine.Debug.Log("could not find one from pool,need to new one");
                        if (SetDefaultGameobject != null)
                        {
                            go = SetDefaultGameobject(prefab); //get prefab default object
                           
                        }
                        else
                        {
                            UnityEngine.Debug.Log("add sharing templete one.");
                            //if not find appropriate,return a default one.
                            go = DEFAULT_GM;
                        }

                        try
                        {
                            if (go != null)
                            {
                                go = GameObject.Instantiate(go) as GameObject;
                                go.gameObject.transform.localPosition = position;
                                go.gameObject.transform.rotation = rotation;
                                obj = (E)(Component)go.transform;
                                objs.Add(obj);
                            }
                        }
                        catch (CException ex)
                        {

                            throw new CException("could not generate a identified game object:" + ex.Message);
                        }

                    }



                }
            }
            return obj;
        }
        public int count(T slot)
        {
            List<E> objs = null;
            if (poolEntries.TryGetValue(slot, out objs))
            {
                return objs.Count;
            }
            return 0;
        }

        public void Recycle(T slot, E obj)
        {
            if (poolEntries.ContainsKey(slot))
            {
                UnityEngine.Debug.Log("find one from pool,need to cache it");
                obj.transform.parent = poolGameObject.transform;
                obj.gameObject.SetActive(false);
                poolEntries[slot].Add(obj);

            }
            else
            {

                UnityEngine.Debug.Log("could not find one from pool,we need to delete it");

                GameObject.Destroy(obj.gameObject);
            }
        }
        public void Recycle(T slot, GameObject obj)
        {
            if (poolEntries.ContainsKey(slot))
            {
                UnityEngine.Debug.Log("find one from pool,need to cache it");

                obj.transform.parent = poolGameObject.transform;
                Component _component = (Component)obj.transform;
                poolEntries[slot].Add((E)_component);
                obj.SetActive(false);

            }
            else
            {
                UnityEngine.Debug.Log("could not find one from pool,need to delete it");
                GameObject.Destroy(obj);
            }
        }

        //clear all existing object in pool
        public void reset()
        {
            StringBuilder sb = new StringBuilder();
            int count = 0;
            if (poolEntries.Count > 0)
            {
                foreach (KeyValuePair<T, List<E>> entry in poolEntries)
                {
                    count++;
                    sb.Append(entry.Key.ToString());
                    sb.Append(entry.Value.ToString());
                    poolEntries.Remove(entry.Key);
                }
                sb.Insert(0, "Clear Total number:" + count);
                UnityEngine.Debug.Log("Pool Manager:" + sb.ToString());
            }
        }

       

    }

}
